#include <tvm/tvm_lexer.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 创建词法解析器
struct tvm_lexer_ctx *lexer_create()
{
    return (struct tvm_lexer_ctx *)calloc(1, sizeof(struct tvm_lexer_ctx));
}

// 销毁词法解析器
void tvm_lexer_destroy(struct tvm_lexer_ctx *lexer)
{
    for (int i = 0; lexer->source_lines[i]; i++)
        free(lexer->source_lines[i]);
    free(lexer->source_lines);

    for (int i = 0; lexer->tokens[i]; i++)
    {
        for (int j = 0; j < MAX_TOKENS; j++)
            free(lexer->tokens[i][j]);

        free(lexer->tokens[i]);
    }

    free(lexer->tokens);
    free(lexer);
}

// 词法分析函数
void tvm_lex(struct tvm_lexer_ctx *lexer, char *source, struct tvm_htab_ctx *defines)
{
    int i, j;
    // C 库函数 char *strtok(char *str, const char *delim) 分解字符串 str 为一组字符串，delim 为分隔符。
    // https://www.runoob.com/cprogramming/c-function-strtok.html
    char *tokp, *linep = strtok(source, "\n"); // 找到出现\n的位置

    /* Split the source into individual lines */
    // 将虚拟机的指令分割成一行行指令
    for (i = 0; linep; i++)
    {
        // 根据行数申请存放指针的空间 2 3 4 5 6 7 ......
        // 思考这里为什么+2，答案是这个for完成后面有一个NULL的赋值操作
        lexer->source_lines = (char **)realloc(lexer->source_lines, sizeof(char *) * (i + 2));
        // 为这个指针申请存放内容的空间
        lexer->source_lines[i] = (char *)calloc(1, strlen(linep) + 1);

        // 将当前行内容复制到新申请的空间
        strcpy(lexer->source_lines[i], linep);

        /* Ignore comments delimited by '#' */
        char *comment_delimiter = strchr(lexer->source_lines[i], '#');
        // 遇到#号，表示这是一行注释，将这个#替换为终止符，忽略后面内容
        if (comment_delimiter)
            *comment_delimiter = 0;

        // 传入NULL值，是因为strtok函数有一个全局变量保存了当前值
        // 该函数会修改第一个参数的值
        // https://blog.csdn.net/chenpy228/article/details/43816273
        // 继续寻找下一个换行符
        linep = strtok(NULL, "\n");
    }

    /* NULL terminate the array to make iteration later easier*/
    lexer->source_lines[i] = NULL;

    /* Split the source into individual tokens */
    // 将一行行指令分割成token
    for (i = 0; lexer->source_lines[i]; i++)
    {
        // lexer->token是一个指针数组
        // lexer->token[i]存放的是一个指针，真实内容存放在*(lexer->token[i])里面
        lexer->tokens = (char ***)realloc(lexer->tokens, sizeof(char **) * (i + 2));
        lexer->tokens[i] = (char **)calloc(MAX_TOKENS, sizeof(char *));

        // 删除逗号
        tokp = strtok(lexer->source_lines[i], " \t,");
        for (j = 0; (tokp && j < MAX_TOKENS); j++)
        {
            char *token = tvm_htab_find_ref(defines, tokp);

            token = token ? token : tokp;
            lexer->tokens[i][j] = calloc(1, (strlen(token) + 1));
            // 将内容拷贝过去
            strcpy(lexer->tokens[i][j], token);
            // 继续寻找下一个制表符加逗号
            tokp = strtok(NULL, " \t,");
        }
    }
    // 逻辑同上
    lexer->tokens[i] = NULL;
    tvm_htab_destroy(defines);
}
